package com.chuangjiangx.member.stored.ddd.application;

import com.chuangjiangx.member.coupon.ddd.application.MbrCouponApplication;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrCoupon;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrHasCoupon;
import com.chuangjiangx.member.coupon.ddd.domain.repository.MbrHasCouponRepository;
import com.chuangjiangx.member.stored.ddd.application.command.*;
import com.chuangjiangx.member.stored.ddd.application.dto.MbrUniformOrdersResult;
import com.chuangjiangx.member.coupon.ddd.application.result.VerifyCouponResult;
import com.chuangjiangx.member.coupon.ddd.domain.service.CouponDomainService;
import com.chuangjiangx.member.basic.ddd.domain.model.MbrAccount;
import com.chuangjiangx.member.basic.ddd.domain.model.MbrAccountRepository;
import com.chuangjiangx.member.basic.ddd.domain.model.Member;
import com.chuangjiangx.member.basic.ddd.domain.model.MemberRepository;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberRedisDomainService;

import com.chuangjiangx.member.stored.ddd.domain.model.MbrRechargeRule;

import com.chuangjiangx.member.stored.ddd.domain.model.RechargeRuleRepository;
import com.chuangjiangx.domain.shared.model.PayEntry;
import com.chuangjiangx.domain.shared.model.PayTerminal;
import com.chuangjiangx.domain.shared.model.PayType;
import com.chuangjiangx.member.stored.ddd.application.dto.MbrPayOrderResult;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrder;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRepository;
import com.chuangjiangx.member.stored.ddd.domain.MbrRandomUtils;
import com.chuangjiangx.member.stored.ddd.domain.stardard.*;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreGrandTotalRule;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreGrandTotalRuleRepository;
import com.chuangjiangx.member.coupon.ddd.domain.model.MbrHasCouponId;
import com.chuangjiangx.member.basic.ddd.domain.model.MemberId;
import com.chuangjiangx.member.basic.ddd.domain.model.MerchantId;
import com.chuangjiangx.member.stored.ddd.domain.exception.MemberPaymentException;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderId;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderStatus;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderType;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrUniformOrderType;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrRechargeRuleId;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * H5支付储值业务
 */
@Service
public class MbrPayApplication {

    @Value("${merchant.web.domain}")
    private String merchantDomain;
    @Value("${member.h5.domain}")
    private String h5Domain;
    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private MbrOrderRepository mbrOrderRepository;
    @Autowired
    private RechargeRuleRepository rechargeRuleRepository;
    @Autowired
    private MemberRedisDomainService memberRedisDomainService;
    @Autowired
    private MbrAccountRepository mbrAccountRepository;
    @Autowired
    private MbrHasCouponRepository mbrHasCouponRepository;
    @Autowired
    private MbrCouponApplication mbrCouponApplication;
    @Autowired
    private CouponDomainService couponDomainService;
    @Autowired
    private MbrScoreGrandTotalRuleRepository mbrScoreGrandTotalRuleRepository;


    /**
     * 下单
     * 1.卡券核销下单
     * 2.会员充值下单
     * 3.会员卡正常支付下单
     * @param command
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public MbrUniformOrdersResult uniformOrders(MbrUniformOrdersCommand command){
        MbrUniformOrdersResult result = new MbrUniformOrdersResult(command.getType());
        MbrOrder mbrOrder;
        Member member = null;
        switch (command.getType()){
            case COUPON:
                //验证卡券码是否有效
                //验证通过后冻结
                //优惠券
                VerifyCouponResult verifyCouponResult = mbrCouponApplication.verifyMbrHasCoupon(command.getCouponCode(), command.getStoreId(), command.getStoreUserId(), command.getAmount());
                MbrCoupon mbrCoupon = verifyCouponResult.getMbrCoupon();
                MbrHasCoupon mbrHasCoupon = verifyCouponResult.getMbrHasCoupon();
                member = memberRepository.fromId(mbrHasCoupon.getMemberId());
                //验证会员
                checkMember(member);
                //创建订单
                mbrOrder = uniformBuildMbrOrder(command, member, mbrHasCoupon.getId(),mbrCoupon, null );
                result.setCouponAmount(mbrCoupon.calcDiscountAmount(command.getAmount()));
                result.setCouponName(mbrCoupon.getName());
                result.setCouponType(mbrCoupon.getType());
                break;
            case RECHARGE:
                //根据充值规则Id下单
                //查询充值规则是否存在,是否启用
                MbrRechargeRule rechargeRule;
                if(-1 == command.getRechargeRuleId()){
                    rechargeRule = MbrRechargeRule.custom(command.getAmount());
                }else{
                    rechargeRule = rechargeRuleRepository.fromId(new MbrRechargeRuleId(command.getRechargeRuleId()));
                    if(rechargeRule == null || !rechargeRule.isEnable()){
                        throw new MemberPaymentException("","储值规则不存在或被禁用");
                    }
                }
                member = memberRepository.fromId(new MemberId(command.getRechargeMemberId()));
                //验证会员
                checkMember(member);
                command.setAmount(rechargeRule.getAmount());
                //创建订单
                mbrOrder = uniformBuildMbrOrder(command, member, null,null, rechargeRule );
                result.setRechargeAmount(rechargeRule.getAmount());
                result.setRechargeRuleName(rechargeRule.getName());
                break;
            case NORMAL:
                member = memberRepository.fromId(new MemberId(command.getConsumerMemberId()));
                //验证会员
                checkMember(member);
                //创建订单
                mbrOrder = uniformBuildMbrOrder(command, member, null,null, null );
                break;
            default:
                //抛出异常
                throw new MemberPaymentException("","下单错误");
        }

        checkMerchantMember(new MerchantId(command.getMerchantId()), member);
        checkOrderAmount(command.getAmount());

        mbrOrderRepository.save(mbrOrder);
        result.setOrderId(mbrOrder.getId().getId());
        result.setOrderNumber(mbrOrder.getOrderNumber());
        result.setOrderAmount(mbrOrder.getOrderAmount());
        result.setNeedPayAmount(mbrOrder.getPaidAmount());
        result.setMemberMobile(member.getMobile());
        return result;
    }

    private MbrOrder uniformBuildMbrOrder(MbrUniformOrdersCommand command, Member member, MbrHasCouponId couponId, MbrCoupon coupon,MbrRechargeRule rule ) {
        MbrOrderType type = Objects.equals(MbrUniformOrderType.RECHARGE,command.getType()) ? MbrOrderType.RECHARGE : MbrOrderType.CONSUME;
        BigDecimal discountAmount = (null == coupon ? BigDecimal.ZERO : coupon.calcDiscountAmount(command.getAmount()));
        return new MbrOrder(
                MbrRandomUtils.generateMbrOrderNumber(),
                type,
                new MerchantId(command.getMerchantId()),
                command.getMerchantUserId(),
                command.getStoreId(),
                command.getStoreUserId(),
                member.getId(),
                command.getAmount(),
                discountAmount,
                command.getAmount().subtract(discountAmount),
                null,
                null,
                null,
                couponId,
                MbrOrderStatus.NOT_PAID,
                null,
                null,
                null,
                null,
                null,
                null == rule ? null : rule.getId()
        );
    }

    /**
     * 使用会员付款码支付会员订单
     * @param command
     */
    @Transactional(rollbackFor = Exception.class)
    public MbrPayOrderResult payOrder(MbrPayOrderCommand command){
        MbrOrder mbrOrder = mbrOrderRepository.fromOrderNumber(command.getOrderNumber());
        if(mbrOrder == null){
            throw new MemberPaymentException("","订单"+command.getOrderNumber()+"不存在");
        }
        if(!Objects.equals(MbrOrderStatus.NOT_PAID,mbrOrder.getStatus())){
            throw new MemberPaymentException("","订单"+command.getOrderNumber()+"状态异常");
        }
        Member member = null;
        MerchantId merchantId = new MerchantId(command.getMerchantId());
        if(BigDecimal.ZERO.compareTo(mbrOrder.getPaidAmount()) == 0){
            //免单支付
            member = memberRepository.fromId(mbrOrder.getMemberId());
        } else {
            //验证付款码
            member = checkMemberWithAuthCode(command.getAuthCode());
            if(mbrOrder.getMemberId().getId() != member.getId().getId()){
                throw new MemberPaymentException("","订单"+command.getOrderNumber()+"不属于会员Id:" + member.getId().getId());
            }
        }
        //验证会员
        checkMember(member);
        //验证会员是否属于当前商户
        checkMerchantMember(merchantId, member);
        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());

        mbrOrder.setPayEntry(PayEntry.MSCARDPAY);
        mbrOrder.setPayType(PayType.SCAN_PAY);
        mbrOrder.setPayTerminal(PayTerminal.getPayTerminal(command.getPayTerminal()));

        MbrPaymentContext context = new MbrPaymentContext();
        context.setMbrOrder(mbrOrder);
        context.setMember(member);
        context.setMbrAccount(mbrAccount);
        MbrScoreGrandTotalRule mbrScoreGrandTotalRule = mbrScoreGrandTotalRuleRepository.fromMerchantId(new MerchantId(command.getMerchantId()));
        context.setMbrScoreGrandTotalRule(mbrScoreGrandTotalRule);
        context.setMerchantAddress(command.getMerchantAddress());
        context.setStoreAddress(command.getStoreAddress());
        StandardMbrPayment payment = new StandardMbrPayment(context);

        MbrPaymentResult mbrPaymentResult = payment.payment();

        MbrPayOrderResult mbrPayOrderResult = new MbrPayOrderResult();
        BeanUtils.copyProperties(mbrPaymentResult,mbrPayOrderResult);
        return mbrPayOrderResult;
    }



    /**
     * 快捷支付
     * 1.使用会员付款码付款
     * 2.使用会员Id+会员后4位手机号码验证付款
     * 3.支持使用卡券
     * 4.支持使用卡券优惠后实付金额为0的订单
     * @param command
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public MbrPayOrderResult quickPay(MbrQuickPayCommand command){
        MerchantId merchantId = new MerchantId(command.getMerchantId());
        //校验会员
        Member member;
        if(null != command.getAuthCode()){
            member = checkMemberWithAuthCode(command.getAuthCode());
        } else {
            member = checkMemberWithIdAndLast4(command.getMemberId(),command.getMemberMobileLast4());
        }
        //验证会员是否属于当前商户
        checkMerchantMember(merchantId, member);
        //校验订单金额不能小于0
        checkOrderAmount(command.getAmount());
        //查询会员账户信息
        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());
        //优惠券
        MbrCoupon mbrCoupon = null;
        //会员的优惠券
        MbrHasCoupon mbrHasCoupon = null;
        if(StringUtils.isNoneBlank(command.getCouponCode())){
            //验证卡券码是否有效
            //验证通过后冻结
            VerifyCouponResult verifyCouponResult = mbrCouponApplication.verifyMbrHasCoupon(command.getCouponCode(), command.getStoreId(), command.getStoreUserId(), command.getAmount());
            mbrCoupon = verifyCouponResult.getMbrCoupon();
            mbrHasCoupon = verifyCouponResult.getMbrHasCoupon();
            //校验优惠券是否属于该会员
            if(!Objects.equals(mbrHasCoupon.getMemberId(),member.getId())){
                throw new MemberPaymentException("", "卡券不属于该会员");
            }
        }

        //创建订单
        MbrOrder mbrOrder = new MbrOrder(
                MbrRandomUtils.generateMbrOrderNumber(),
                MbrOrderType.CONSUME,
                merchantId,
                command.getMerchantUserId(),
                command.getStoreId(),
                command.getStoreUserId(),
                member.getId(),
                command.getAmount(),
                //计算优惠金额
                mbrHasCoupon == null ? BigDecimal.ZERO : mbrCoupon.calcDiscountAmount(command.getAmount()),
                //计算实际需要支付的金额
                mbrHasCoupon == null ? command.getAmount() : mbrCoupon.calcRealPayAmount(command.getAmount()),
                null,
                null,
                null,
                mbrHasCoupon == null ? null : mbrHasCoupon.getId(),
                MbrOrderStatus.NOT_PAID,
                PayEntry.MSCARDPAY,
                PayType.SCAN_PAY,
                null,
                PayTerminal.getPayTerminal(command.getPayTerminal()),
                null,
                null
        );

        MbrPaymentContext context = new MbrPaymentContext();
        context.setMbrOrder(mbrOrder);
        context.setMember(member);
        context.setMbrAccount(mbrAccount);
        MbrScoreGrandTotalRule mbrScoreGrandTotalRule = mbrScoreGrandTotalRuleRepository.fromMerchantId(new MerchantId(command.getMerchantId()));
        context.setMbrScoreGrandTotalRule(mbrScoreGrandTotalRule);
        context.setMerchantAddress(command.getMerchantAddress());
        context.setStoreAddress(command.getStoreAddress());
        StandardMbrPayment payment = new StandardMbrPayment(context);
        MbrPaymentResult mbrPaymentResult = payment.payment();

        //设置支付结果
        MbrPayOrderResult mbrPayOrderResult = new MbrPayOrderResult();
        BeanUtils.copyProperties(mbrPaymentResult,mbrPayOrderResult);
        return mbrPayOrderResult;
    }

    /**
     * 网页支付(C扫B),可能有优惠券
     * @param command
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public MbrPayOrderResult webPagePay(MbrWebPagePayCommand command){
        //校验订单金额
        checkOrderAmount(command.getAmount());
        MerchantId merchantId = new MerchantId(command.getMerchantId());
        Member member = memberRepository.fromId(new MemberId(command.getMemberId()));
        checkMember(member);
        //验证会员是否属于当前商户
        checkMerchantMember(merchantId, member);
        //优惠金额
        BigDecimal discountAmount = BigDecimal.ZERO;
        //优惠券
        MbrHasCouponId mbrHasCouponId = null;
        if(StringUtils.isNoneBlank(command.getCouponCode())){

            //验证卡券码是否有效
            //验证通过后冻结
            VerifyCouponResult verifyCouponResult = mbrCouponApplication.verifyMbrHasCoupon(command.getCouponCode(), command.getStoreId(), command.getStoreUserId(), command.getAmount());
            //优惠券
            MbrCoupon mbrCoupon = verifyCouponResult.getMbrCoupon();
            MbrHasCoupon mbrHasCoupon = verifyCouponResult.getMbrHasCoupon();
            //校验优惠券是否属于该会员
            if(!Objects.equals(mbrHasCoupon.getMemberId(),member.getId())){
                throw new MemberPaymentException("", "卡券不属于该会员");
            }
            mbrHasCouponId = mbrHasCoupon.getId();
            //计算优惠金额
            discountAmount = mbrCoupon.calcDiscountAmount(command.getAmount());
        }

        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());

        //创建订单
        MbrOrder mbrOrder = new MbrOrder(
                MbrRandomUtils.generateMbrOrderNumber(),
                MbrOrderType.CONSUME,
                merchantId,
                command.getMerchantUserId(),
                command.getStoreId(),
                command.getStoreUserId(),
                member.getId(),
                command.getAmount(),
                discountAmount,
                command.getAmount().subtract(discountAmount),
                null,
                null,
                null,
                mbrHasCouponId,
                MbrOrderStatus.NOT_PAID,
                PayEntry.MSCARDPAY,
                PayType.SCAN_PAY,
                null,
                PayTerminal.getPayTerminal(command.getPayTerminal()),
                null,
                null
        );
        MbrPaymentContext paymentParams = new MbrPaymentContext();
        paymentParams.setMbrOrder(mbrOrder);
        paymentParams.setMember(member);
        paymentParams.setMbrAccount(mbrAccount);
        StandardMbrPayment payment = new StandardMbrPayment(paymentParams);

        MbrPaymentResult mbrPaymentResult = payment.payment();

        MbrPayOrderResult mbrPayOrderResult = new MbrPayOrderResult();
        BeanUtils.copyProperties(mbrPaymentResult,mbrPayOrderResult);
        return mbrPayOrderResult;
    }

    /**
     * 撤销订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void undo(MbrOrderUndoCommand command){
        MbrOrder mbrOrder = mbrOrderRepository.fromId(new MbrOrderId(command.getMbrOrderId()));
        if(null == mbrOrder){
            throw new MemberPaymentException("", "订单不存在");
        }
        mbrOrder.undo(command.getMerchantId(),command.getStoreId(),command.getStoreUserId(),command.getUserType());
        //如果有使用卡券,卡券需要解冻
        if(null != mbrOrder.getMbrHasCouponId()){
            MbrHasCoupon mbrHasCoupon = mbrHasCouponRepository.fromId(mbrOrder.getMbrHasCouponId());
            couponDomainService.unlockCoupon(mbrHasCoupon.getVerifyCode());
        }
        mbrOrderRepository.update(mbrOrder);
    }

    /**
     * 会员消费记录-编辑备注
     */
    @Transactional(rollbackFor = Exception.class)
    public void editRemark(MbrOrderEditRemarkCommand command){
        MbrOrder mbrOrder = mbrOrderRepository.fromId(new MbrOrderId(command.getId()));
        if(null == mbrOrder){
            throw new MemberPaymentException("", "订单不存在");
        }
        mbrOrder.editRemark(command.getRemark());
        mbrOrderRepository.update(mbrOrder);
    }


    private void checkOrderAmount(BigDecimal amount) {
        if (BigDecimal.ZERO.compareTo(amount) > 0) {
            throw new MemberPaymentException("", "订单金额异常");
        }
    }

    /**
     * 根据会员付款码获取并且校验会员
     * @param authCode
     * @return
     */
    private Member checkMemberWithAuthCode(String authCode) {

        String midStr = memberRedisDomainService.getKeyValueAndDel(authCode);
        if (StringUtils.isBlank(midStr)) {
            throw new MemberPaymentException("", "无效的会员付款码");
        }
        Long memeberId = Long.valueOf(midStr);
        Member member = memberRepository.fromId(new MemberId(memeberId));

        if(member == null){
            throw new MemberPaymentException("","会员不存在");
        }
        checkMember(member);
        return member;
    }

    /**
     * 根据会员ID和会员手机号码后4位获取并且校验会员
     * @param memberId
     * @param memberMobileLast4
     * @return
     */
    private Member checkMemberWithIdAndLast4(Long memberId, String memberMobileLast4) {

        Member member = memberRepository.fromId(new MemberId(memberId));
        if(member == null){
            throw new MemberPaymentException("","会员不存在");
        }
        checkMember(member);
        String mobile = member.getMobile();
        if(StringUtils.isBlank(mobile)){
            throw new MemberPaymentException("","会员手机号码不存在,无法发起支付!");
        }
        if(!mobile.endsWith(memberMobileLast4)){
            throw new MemberPaymentException("","会员手机号码后4位校验不通过!");
        }
        return member;
    }

    private void checkMerchantMember(MerchantId merchantId, Member member) {
        if(!Objects.equals(member.getOperationInfo().getMerchantId(),merchantId)){
            throw new MemberPaymentException("","会员非该商户下");
        }
    }

    private void checkMember(Member member){
        if(member == null || !member.isEnable()){
            throw new MemberPaymentException("","会员不存在或被禁用");
        }
    }




}
